#include <audio/riff.hpp>

#include <efi/device_path.hpp>
#include <efi/file.hpp>
#include <efi/memory.hpp>
#include <efi/protocol.hpp>
#include <efi/rt.hpp>

using namespace efi;

namespace audio::Riff {

bool compareChunkId(void *p, const char *signature) {
  return *(uint32_t *)p == *(uint32_t *)signature;
}

audio::Audio readFromFile(const char16_t *path) {
  RiffChunk *riffChunk = nullptr;
  RiffChunk *formatChunk = nullptr;
  RiffChunk *dataChunk = nullptr;

  printf(u"opening audio file %s...\r\n", path);

  auto file = File::open(path, EFI_FILE_MODE_READ, 0);
  auto fileInfo = file->getInfo();
  auto size = fileInfo->fileSize;

  if (size == 0) {
    print(u"failed to open file\r\n");
    return Audio();
  }

  uint8_t *buffer
      = (uint8_t *)Memory::allocPool(Memory::Type::LoaderData, size);

  file->_read(file, &size, (void *)buffer);

  riffChunk = (RiffChunk *)buffer;

  if (!compareChunkId(riffChunk->id, RIFF_CHUNK_ID)
      || !compareChunkId(riffChunk->data, WAVE_CHUNK_ID)) {
    print(u"invalid wav file\r\n");
    while (1)
      ;
  }

  for (uint64_t i = sizeof(RiffChunk); i < size; ++i) {
    if (formatChunk && dataChunk)
      break;

    if (!formatChunk && compareChunkId(buffer + i, WAVE_FORMAT_CHUNK_ID)) {
      formatChunk = (RiffChunk *)(buffer + i);
    }

    if (!dataChunk && compareChunkId(buffer + i, WAVE_DATA_CHUNK_ID)) {
      dataChunk = (RiffChunk *)(buffer + i);
    }
  }

  if (!formatChunk || !dataChunk) {
    print(u"chunk not found\r\n");
    while (1)
      ;
  }

  WaveFormatData *format = (WaveFormatData *)formatChunk->data;

  return audio::Audio{
      .rawData = buffer,
      .format{
          .channelCount = format->channels,
          .bitsPerSample = format->bitsPerSample,
          .samplesPerSec = format->samplesPerSec
      },
      .samples = dataChunk->data,
      .size = dataChunk->size
  };
}

} // namespace audio::Riff